package com.yrline.pay.util.httpclient;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;



import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
 * 
 * @author fanrui
 *
 */
public class HttpsAction  {

	private Logger logger = LoggerFactory.getLogger(getClass());

	private final Map<String, CloseableHttpClient> httpClients = new HashMap<String, CloseableHttpClient>();

	private ElideHttpClientGenerator httpClientGenerator = new ElideHttpClientGenerator();

	private static HttpsAction action = new HttpsAction();
	
	private HttpsAction(){}
	
	public static HttpsAction getInstance(){
		return action;
	}
	
	private CloseableHttpClient getHttpClient(Site site) {
		if (site == null) {
			return httpClientGenerator.getClient(null);
		}
		String domain = site.getDomain();
		CloseableHttpClient httpClient = httpClients.get(domain);
		if (httpClient == null) {
			synchronized (this) {
				httpClient = httpClients.get(domain);
				if (httpClient == null) {
					httpClient = httpClientGenerator.getClient(site);
					httpClients.put(domain, httpClient);
				}
			}
		}
		return httpClient;
	}

	public Response doRequest(Request request,Site site) {
		
		
		String charset = "UTF-8";
		Map<String, String> headers = request.getHeaders();
//		if (site != null) {
//			charset = site.getCharset();
//			//headers = site.getHeaders();
//		} 
		logger.info(request.toString());
		CloseableHttpResponse httpResponse = null;
		int statusCode = 0;
		try {
			HttpUriRequest httpUriRequest = getHttpUriRequest(request, site,
					headers);
			httpResponse = getHttpClient(site).execute(httpUriRequest);
			statusCode = httpResponse.getStatusLine().getStatusCode();
			if (statusCode==200) {
				Response page = handleResponse(request, charset, httpResponse);
				logger.info(page.getRawText());
				return page;
			} else {
				logger.warn("code error " + statusCode + "\t"
						+ request.getUrl());
				return null;
			}
		} catch (IOException e) {
			logger.warn("doRequest page " + request.getUrl() + " error", e);
			
			return null;
		} finally {
			try {
				if (httpResponse != null) {
					// ensure the connection is released back to pool
					EntityUtils.consume(httpResponse.getEntity());
				}
			} catch (IOException e) {
				logger.warn("close response fail", e);
			}
		}
	}

	public Response doDownLoadFile(Request request,Site site){

		
		
		Map<String, String> headers = request.getHeaders();

		logger.info(request.toString());
		CloseableHttpResponse httpResponse = null;
		int statusCode = 0;
		try {
			HttpUriRequest httpUriRequest = getHttpUriRequest(request, site,
					headers);
			httpResponse = getHttpClient(site).execute(httpUriRequest);
			statusCode = httpResponse.getStatusLine().getStatusCode();
			 HttpEntity entity = httpResponse.getEntity();
	            if (statusCode >= 300) {
	                EntityUtils.consume(entity);
	                logger.warn("code error " + statusCode + "\t"
							+ request.getUrl());
	                throw null;
	            } else {
	            	Response page = new Response();
	        		page.setUrl(request.getUrl());
	        		page.setRequest(request);
	        		page.setStatusCode(httpResponse.getStatusLine().getStatusCode());
	        		page.setHttpResponse(httpResponse);
	        		String fileName=null;
	                Header[] dispositionHeaders = httpResponse.getHeaders("Content-disposition");
	                if (dispositionHeaders != null && dispositionHeaders.length > 0) {
	                     fileName = extractFileName(dispositionHeaders[0].getValue());
	                    
	                    
	                } 
	                if (fileName == null || "".equals(fileName.trim())) {
                        logger.warn("Cannot get filename from Content-disposition");
                        fileName = UUID.randomUUID().toString();
                    }
	                InputStream inputStream = entity.getContent();
                    File tempFile = new File(FileUtils.getTempDirectory(), fileName);
                    FileUtils.copyInputStreamToFile(inputStream, tempFile);
                    page.setFile(tempFile);
                    return page;
	                
	            }
	            
	            
			
		} catch (IOException e) {
			logger.warn("doRequest page " + request.getUrl() + " error", e);
			
			return null;
		} finally {
			try {
				if (httpResponse != null) {
					// ensure the connection is released back to pool
					EntityUtils.consume(httpResponse.getEntity());
				}
			} catch (IOException e) {
				logger.warn("close response fail", e);
			}
		}
	
	} 
	 private String extractFileName(String headerValue) {
	        String fileName = null;
	        Pattern regex = Pattern.compile("(?<=filename=\").*?(?=\")");
	        Matcher regexMatcher = regex.matcher(headerValue);
	        if (regexMatcher.find()) {
	            fileName = regexMatcher.group();
	        }

	        return fileName;
	    }
	protected boolean statusAccept(Set<Integer> acceptStatCode, int statusCode) {
		return acceptStatCode.contains(statusCode);
	}

	protected HttpUriRequest getHttpUriRequest(Request request, Site site,
			Map<String, String> headers) {
		RequestBuilder requestBuilder = selectRequestMethod(request).setUri(
				request.getUrl());
		if (headers != null) {
			for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
				requestBuilder.addHeader(headerEntry.getKey(),
						headerEntry.getValue());
			}
		}
		RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
				.setConnectionRequestTimeout(request.getTimeout())
				.setSocketTimeout(request.getTimeout())
				.setConnectTimeout(request.getTimeout())
				.setCookieSpec(CookieSpecs.BEST_MATCH);
		
		requestBuilder.setConfig(requestConfigBuilder.build());
		return requestBuilder.build();
	}

	protected RequestBuilder selectRequestMethod(Request request) {
		String method = request.getMethod();
		if (method == null || method.equalsIgnoreCase(HttpConstant.Method.GET)) {
			// default get
			return RequestBuilder.get();
		} else if (method.equalsIgnoreCase(HttpConstant.Method.POST)) {
			RequestBuilder requestBuilder = RequestBuilder.post();
			requestBuilder.setEntity(request.getEntity());
			return requestBuilder;
		} else if (method.equalsIgnoreCase(HttpConstant.Method.HEAD)) {
			return RequestBuilder.head();
		} else if (method.equalsIgnoreCase(HttpConstant.Method.PUT)) {
			return RequestBuilder.put();
		} else if (method.equalsIgnoreCase(HttpConstant.Method.DELETE)) {
			return RequestBuilder.delete();
		} else if (method.equalsIgnoreCase(HttpConstant.Method.TRACE)) {
			return RequestBuilder.trace();
		}
		throw new IllegalArgumentException("Illegal HTTP Method " + method);
	}

	protected Response handleResponse(Request request, String charset,
			HttpResponse httpResponse) throws IOException {
		String content = getContent(charset, httpResponse);
		Response page = new Response();
		page.setRawText(content);
		page.setUrl(request.getUrl());
		page.setRequest(request);
		page.setStatusCode(httpResponse.getStatusLine().getStatusCode());
		page.setHttpResponse(httpResponse);
		return page;
	}

	protected String getContent(String charset, HttpResponse httpResponse)
			throws IOException {
		if (charset == null) {
			byte[] contentBytes = IOUtils.toByteArray(httpResponse.getEntity()
					.getContent());
			String htmlCharset = "UTF-8";
			
			return new String(contentBytes, htmlCharset);
			
		} else {
			return IOUtils.toString(httpResponse.getEntity().getContent(),
					charset);
		}
	}

	
}
